/* NOTICE
    OpenJaWS - Open Java Weather Station
    
    Copyright (C) 2008 Grant Gardner <grant@lastweekend.com.au>
    
    OpenJaWS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
NOTICE */
package au.com.lastweekend.openjaws;

import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.lastweekend.openjaws.api.Readings;
import au.com.lastweekend.openjaws.api.WeatherPlugin;
import au.com.lastweekend.openjaws.api.WeatherPluginException;
import au.com.lastweekend.openjaws.api.WeatherStation;
import au.com.lastweekend.openjaws.api.WeatherStationException;
import au.com.lastweekend.openjaws.api.WeatherStation.HistoryIterator;
import au.com.lastweekend.openjaws.util.LoopSleepThread;

public class WeatherThread extends LoopSleepThread {
    private static final Logger LOG = LoggerFactory.getLogger(WeatherThread.class);

    private WeatherStation ws;
    private List<WeatherPlugin> plugins;

    private long maxDrift = 5;
    private boolean skipHistory = false;

    private Readings readings = new Readings();

    public WeatherThread(WeatherStation ws, List<WeatherPlugin> plugins) {

        super("WeatherThread",LOG);
        this.ws = ws;
        this.plugins = plugins;

    }

    @Override
    protected void inLoop() {

        Date currentTime = adjustTime();
        readings.setReadingTime(currentTime);
        fetchCurrent(readings);
        notifyPlugins(readings);
    }

    @Override
    protected void beforeLoop() {

        if (!isSkipHistory()) {
            
            HistoryIterator histIterator = ws.historyIterator(null);
            
            while (histIterator.hasNext()) {
                 histIterator.next(readings);
                 notifyPlugins(readings);
            }
            

        } else {
            LOG.info("Skipping history");
        }
        
    }

    private void fetchCurrent(Readings readings) {

        LOG.info(String.format("Fetching current readings"));
        try {
            ws.fetchReadings(readings);
        } catch (WeatherStationException e1) {
            LOG.error("Exception fetching current readings", e1);
        }
    }

    private Date adjustTime() {

        Date localTime;
        try {
            Date wsTime = ws.getCurrentTime();
            long wsTimeMillis = wsTime.getTime();
            long currentTimeMillis = System.currentTimeMillis();
            localTime = new Date(currentTimeMillis);

            long drift = currentTimeMillis - wsTimeMillis;
            LOG.debug(String.format("Time drift - %d ms", drift));
            if (Math.abs(drift) > (maxDrift * 1000)) {
                LOG.warn(String.format("Station time (%tc) out of synch with local time (%tc)", wsTime, localTime));
                ws.setCurrentTime(new Date(currentTimeMillis));
            }
        } catch (WeatherStationException e) {
            LOG.error("Unable to synchronise station time", e);
            localTime = new Date();
        }

        return localTime;

    }

    private void notifyPlugins(Readings readings) {

        if (readings != null) {
            for (WeatherPlugin plugin : plugins) {
                try {
                    LOG.debug(String.format("Notifying plugin %s", plugin.toString()));
                    plugin.process(readings);
                } catch (WeatherPluginException e) {
                    LOG.error("Plugin " + plugin + " update failed", e);
                }
            }
        }

    }

    @Override
    protected int delay() {

        int secondsToReading = 0;
        try {
            secondsToReading = ws.getReadDelay();
        } catch (WeatherStationException e1) {
            LOG.warn("Exception fetching read delay", e1);

        }
        return secondsToReading;
    }

    /**
     * Maximum clock drift in milliseconds before we try to reset the station clock
     * 
     * @return the maxDrift
     */
    public long getMaxDrift() {

        return maxDrift;
    }

    /**
     * Set the maximum drift in seconds before we try to reset the station clock
     * 
     * @param maxDrift
     *            the maxDrift to set - default 5 seconds
     */
    public void setMaxDrift(long maxDrift) {

        this.maxDrift = maxDrift;
    }

    public boolean isSkipHistory() {

        return skipHistory;
    }

    /**
     * 
     * @param skipHistory -
     *            set to true to bypass reading history values
     */
    public void setSkipHistory(boolean skipHistory) {

        this.skipHistory = skipHistory;
    }

}
